#include <gtest/gtest.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>

using namespace ::std;

// v01
// class Solution {
// public:
//     vector<vector<int>> threeSum(vector<int>& nums) {
//         int size = nums.size();
//         sort(nums.begin(), nums.end());
//         vector<vector<int>> res;
//         for(int i = 0; i < size - 2; ++i) {
//             while(i != 0 && i < size - 2 && nums[i] == nums[i - 1]) ++i;
//             if(i == size - 2 || nums[i] > 0) break;
//             for(int j = i + 1, k = size - 1; j < k && (nums[i] + nums[j] <= 0) && nums[k] >= 0; ) {
//                 int sum = nums[i] + nums[j] + nums[k];
//                 if(sum < 0) ++j;
//                 else if(sum > 0) --k;
//                 else if(sum == 0) {
//                     res.push_back({nums[i], nums[j], nums[k]});
//                     ++j; --k;
//                     while(j < k && nums[j] == nums[j - 1]) ++j;
//                     while(j < k && nums[k] == nums[k + 1]) --k;
//                 }
//             }
//         }
//         return res;
//     }
// };

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums);
};

vector<vector<int>> Solution::threeSum(vector<int>& nums) {
    int size = nums.size(); // 把结果强转成有符号整型
    sort(nums.begin(), nums.end());
    vector<vector<int>> result = {};
    for(int i = 0; i < size - 2; ++i) {
        int target = 0 - nums[i];
        for(int j = i + 1, k = size - 1; j < k && nums[i] + nums[j] <= 0 && nums[k] >= 0; ++j) {
            if(nums[i] + nums[j] > 0 || nums[k] < 0) break;
            int balance = target - nums[j];
            while(j < k && nums[k] > balance) --k;
            if(j < k && nums[k] == balance) result.push_back({nums[i], nums[j], nums[k]});
            // 防止连续相等的值返回相同的结果
            while(j < k && nums[j] == nums[j + 1]) ++j;
            // while(j < k - 1 && nums[k] == nums[k - 1]) --k;
        }
        while(i < size - 2 && nums[i] == nums[i + 1]) ++i;
    }
    return result;
}

class ThreeSumCheck : public ::testing::Test {
public:
	class Solution solution;
};

TEST_F(ThreeSumCheck, testThreeSum_1) {
    vector<int> nums = {-1,0,1,2,-1,-4};
    vector<vector<int>> out = solution.threeSum(nums);
    EXPECT_TRUE(vector<int>({-1,-1,2}) == out[0]);
    EXPECT_TRUE(vector<int>({-1,0,1}) == out[1]);
}

TEST_F(ThreeSumCheck, testThreeSum_2) {
    vector<int> nums = {};
    vector<vector<int>> out = solution.threeSum(nums);
    EXPECT_TRUE(out.empty());
}

TEST_F(ThreeSumCheck, testThreeSum_3) {
    vector<int> nums = {0};
    vector<vector<int>> out = solution.threeSum(nums);
    EXPECT_TRUE(out.empty());
}

TEST_F(ThreeSumCheck, testThreeSum_4) {
    vector<int> nums = {-2,-2,1,1};
    vector<vector<int>> out = solution.threeSum(nums);
    EXPECT_TRUE(vector<int>({-2,1,1}) == out[0]);
}

TEST_F(ThreeSumCheck, testThreeSum_5) {
    vector<int> nums = {-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0};
    vector<vector<int>> out = solution.threeSum(nums);
    EXPECT_TRUE(vector<int>({-5,1,4}) == out[0]);
    EXPECT_TRUE(vector<int>({-4,0,4}) == out[1]);
    EXPECT_TRUE(vector<int>({-4,1,3}) == out[2]);
    EXPECT_TRUE(vector<int>({-2,-2,4}) == out[3]);
    EXPECT_TRUE(vector<int>({-2,1,1}) == out[4]);
    EXPECT_TRUE(vector<int>({0,0,0}) == out[5]);
}

int main() {
	::testing::InitGoogleTest();
	return RUN_ALL_TESTS();
}
